package com.spx.core.services;

import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;

import com.spx.core.types.Activity;
import com.spx.core.types.Role;
import com.spx.core.types.User;
import com.spx.kernel.service.NonBusiness;
import com.spx.kernel.service.PersistService;
import com.spx.kernel.service.UpdateListener;
import com.spx.kernel.types.ModelType;

public class SecurityServiceImpl implements SecurityService, UpdateListener
{
	public static final String CLASS = "class";
	
	
	
	Set<WeakReference<LoginListener>> loginListeners = new HashSet<WeakReference<LoginListener>>(); 
	
	
	@Override
	public void addLoginListener(LoginListener listener){
		WeakReference<LoginListener> ref = new WeakReference<LoginListener>(listener);
		loginListeners.add(ref);
	}
	
	
	private void fireLoginAction(){
		Set<WeakReference<LoginListener>> removalList = new HashSet<WeakReference<LoginListener>>();
		for (WeakReference<LoginListener> ref :loginListeners)
		{
		    	LoginListener listener = ref.get();
		    	if (listener == null){
		    		removalList.add(ref);
		    		continue;
		    	}
		    	listener.loginActivity();
		}
		loginListeners.removeAll(removalList);
	}
	

	UserSessionHolder presentUserHolder = new BasicUserSessionHolder();

//	private static final String FETCH_ALL_USERS = "select u from " + User.class.getName() + " u";
//	private static final String FETCH_ALL_ROLES = "select u from " + Role.class.getName() + " u";
	private static final String FETCH_ALL_ACTIONS = "select u from " + Activity.class.getName() + " u";

	private final String SYSADM = "root";
	private User sysadm;

	PersistService persistService;

	public SecurityServiceImpl(final PersistService persistService)
	{

		this.persistService = persistService;
		persistService.addChangeListener(User.class, this);
		persistService.addChangeListener(Role.class, this);
		persistService.addChangeListener(Activity.class, this);
		getUserSessionHolderOverride();
		validateUser();
	}

	Set<SecurityListener> listeners = new HashSet<SecurityListener>();

	private com.spx.core.types.User buildUser(final String userName, final String password)
	{
		final User user = new com.spx.core.types.User();
		user.setName(userName);
		user.setPassword(password);
		return user;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.spx.core.starttypes.service.SecurityService#addUser(java.lang.String,
	 * java.lang.String)
	 */
	public User addUser(final String userName, final String password)
	{

		final User user = buildUser(userName, password);
		persistService.persist(user);
		notifyListenersCreate(user);

		return user;
	}

	private void notifyListenersDelete(User user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.deletingUser(user); // can modify the user here
		}
	}

	private void notifyListenersCreate(User user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.creatingUser(user); // can modify the user here
		}
	}

	private void notifyListenersUpdate(User user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.updateUser(user); // can modify the user here
		}
	}

	private void notifyListenersDelete(Role user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.deletingRole(user); // can modify the user here
		}
	}

	private void notifyListenersCreate(Role user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.creatingRole(user); // can modify the user here
		}
	}

	private void notifyListenersUpdate(Role user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.updateRole(user); // can modify the user here
		}
	}

	private void notifyListenersDelete(Activity user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.deletingAction(user); // can modify the user here
		}
	}

	private void notifyListenersCreate(Activity user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.creatingAction(user); // can modify the user here
		}
	}

	private void notifyListenersUpdate(Activity user)
	{
		for (final SecurityListener listener : listeners)
		{
			listener.updateAction(user); // can modify the user here
		}
	}

	
	
	
	
	
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.spx.core.starttypes.service.SecurityService#addListener(com.spx.core
	 * .starttypes.service.SecurityListener)
	 */
	@NonBusiness
	public void addListener(final SecurityListener listener)
	{
		listeners.add(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.spx.core.starttypes.service.SecurityService#removeListener(com.spx
	 * .core.starttypes.service.SecurityListener)
	 */
	public void removeListener(final SecurityListener listener)
	{
		listeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.spx.core.starttypes.service.SecurityService#findUser(java.lang.String
	 * )
	 */

	public User findUser(final String userName)
	{
		User result = null; 
		if (persistService != null)
		{
			result = persistService.findByName(userName, User.class);
			
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.spx.core.starttypes.service.SecurityService#fetchAllUsers()
	 */
	public Collection<User> fetchAllUsers()
	{
		final Set<User> userResult = persistService.findAll(User.class);
		return userResult;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.spx.core.starttypes.service.SecurityService#fetchAllUsers()
	 */
	@SuppressWarnings("unchecked")
	public Collection<Activity> fetchAllActions()
	{
		final String findQuery = FETCH_ALL_ACTIONS;
		final Query query = persistService.getEntityManager().createQuery(findQuery);
		final List<Activity> preResult = query.getResultList();
		final Set<Activity> result = new HashSet<Activity>(preResult);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.spx.core.starttypes.service.SecurityService#update(com.spx.core.
	 * starttypes.User)
	 */
	public void update(final User user)
	{
		persistService.getEntityManager().merge(user);
		validateUser();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.spx.core.starttypes.service.SecurityService#update(com.spx.core.
	 * starttypes.User)
	 */
	public void update(final Role role)
	{
		persistService.update(role);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.spx.core.starttypes.service.SecurityService#delete(com.spx.core.
	 * starttypes.User)
	 */
	public void delete(final User user)
	{
		if (user != sysadm)
		{
			persistService.getEntityManager().remove(user);
			notifyListenersDelete(user);
		}
		validateUser();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.spx.core.starttypes.service.SecurityService#delete(com.spx.core.
	 * starttypes.User)
	 */
	public void delete(final Role role)
	{
		persistService.getEntityManager().remove(role);
		notifyListenersDelete(role);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.spx.core.starttypes.service.SecurityService#addUser(com.spx.core.
	 * starttypes.User)
	 */
	public void addUser(final User user)
	{

		if (user != sysadm)
		{
			validateUser();
		}
		if (user.getName() == null || user.getName().trim().length() == 0)
		{
			throw new IllegalArgumentException(user.getName());
		}
		if (persistService != null && persistService.getEntityManager() != null)
		{
			persistService.persist(user);
			
			notifyListenersCreate(user);
		}
	}

	@Override
	public void addRole(final Role role)
	{

		if (StringUtils.isBlank(role.getName()))
		{
			throw new IllegalArgumentException(role.getName());
		}
		persistService.getEntityManager().persist(role);
		notifyListenersCreate(role);

	}

	private void validateUser()
	{
		sysadm = findUser(SYSADM);
		if (sysadm == null)
		{
			sysadm = new User();
			sysadm.setName(SYSADM);
			// sysadm.setLocale(Locale.getDefault());
			sysadm.setPassword(SYSADM);
			addUser(sysadm);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.spx.core.starttypes.service.SecurityService#fetchAllUsers(int,
	 * int)
	 */
	
	public Set<User> fetchAllUsers(final int startAt, final int pageSize)
	{
	
		final Set<User> result =persistService.findAll(User.class);


		return result;
	}



	@Override
	public User modify(User user)
	{
		final User t = persistService.getEntityManager().merge(user);

		return t;
	}

	@Override
	public void updated(ModelType model)
	{
		if (model instanceof User)
		{
			notifyListenersUpdate((User) model);
		} else if (model instanceof Role)
		{
			notifyListenersUpdate((Role) model);
		} else if (model instanceof Activity)
		{
			notifyListenersUpdate((Activity) model);
		}

	}

	@Override
	public Set<Role> fetchAllRoles(int startAt, int pageSize)
	{
		final Set<Role> result =persistService.findAll(Role.class);
		return result;

	}

	@Override
	public void addAction(Activity modelObject)
	{
		persistService.getEntityManager().persist(modelObject);
		notifyListenersCreate(modelObject);
	}

	@SuppressWarnings("unchecked")
	@Override
	@ActivityLabel(name="fetch-all-activities")
	public Set<Activity> fetchAllActions(int startAt, int pageSize)
	{
		final String findQuery = FETCH_ALL_ACTIONS;
		final Query query = persistService.getEntityManager().createQuery(findQuery);
		query.setFirstResult(startAt);
		query.setMaxResults(pageSize);
		final List<Activity> preResult = query.getResultList();
		final Set<Activity> result = new HashSet<Activity>(preResult);
		return result;
	}

	@Override
	public void deleteActionFromRole(Activity activity, Role role)
	{
		role.remove(activity);
		persistService.update(role);
		updated(role);
	}

	@Override
	public void addActionToRole(Activity activity, Role role)
	{
		role.add(activity);
		persistService.update(role);
		updated(role);
	}

	@Override
	public void delete(Activity model)
	{
		persistService.getEntityManager().remove(model);
		notifyListenersDelete(model);
	}

	@Override
	public void modify(Role role)
	{
		persistService.getEntityManager().merge(role);
		notifyListenersUpdate(role);
	}

	@Override
	public boolean login(String userName, String password)
	{
		boolean result = false;
		User user = persistService.findByName(userName, User.class);
		if (user != null){
			this.presentUserHolder.setUser(user);
			fireLoginAction();
			result = true;
		}
		else if (StringUtils.equals(SYSADM, userName)){
			if (persistService.getEntityManager()== null)
			{
				User tempRoot = new User();
				tempRoot.setName(SYSADM);
				this.presentUserHolder.setUser(tempRoot);
				fireLoginAction();
				result = true; // cannot do much but.
			}
		}
		return result;
	}

	
	public void logout(){
		this.presentUserHolder.setUser(null);
	}
	
	@Override
	public User getPresentUser()
	{
		User user = presentUserHolder.getUser();
		return user;
	}

	private static final String USER_SESSION_INFO_EXTENSIONPOINT_ID = "com.spx.core.session.user";

	private void getUserSessionHolderOverride()
	{

		final IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(USER_SESSION_INFO_EXTENSIONPOINT_ID);
		final IExtension[] extensions = extensionPoint.getExtensions();
		for (final IExtension extension : extensions)
		{
			final IConfigurationElement[] configs = extension.getConfigurationElements();
			for (final IConfigurationElement config : configs)
			{
				try
				{
					final UserSessionHolder sessionHolder = (UserSessionHolder) config.createExecutableExtension(CLASS);
					if (sessionHolder != null)
					{
						this.presentUserHolder = sessionHolder;
					}

				} catch (final CoreException e)
				{
					e.printStackTrace();
				}
			}

		}
	}

	@Override
	public Activity findActivity(String activityLabel)
	{
		Activity result = persistService.findByName(activityLabel, Activity.class);
		return result;
	}

	
}
